using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Content.Pipeline;
using Microsoft.Xna.Framework.Content.Pipeline.Graphics;
using Microsoft.Xna.Framework.Content.Pipeline.Processors;

namespace ExtendedPipelines
{
    /// <summary>
    /// This class will be instantiated by the XNA Framework Content Pipeline
    /// to apply custom processing to content data, converting an object of
    /// type TInput to TOutput. The input and output types may be the same if
    /// the processor wishes to alter data without changing its type.
    ///
    /// This should be part of a Content Pipeline Extension Library project.
    ///
    /// TODO: change the ContentProcessor attribute to specify the correct
    /// display name for this processor.
    /// </summary>
    [ContentProcessor(DisplayName = "ExtendedPipelines - Model Processor")]
    public class ExtendedModelProcessor : ModelProcessor
    {
        const int MaxBonesCount = 59;

        public override ModelContent Process(NodeContent input, ContentProcessorContext context)
        {
            ModelContent model = base.Process(input, context);

            BoneContent skeleton = MeshHelper.FindSkeleton(input);

            // Only process skeleton if the model have it
            if (skeleton != null)
            {
                // bake everything so we get the same coord
                FlattenTransform(input, skeleton);

                // read the bind pose
                IList<BoneContent> bones = MeshHelper.FlattenSkeleton(skeleton);
                if (bones.Count > MaxBonesCount)
                {
                    throw new InvalidContentException("Has more bone then Maximum Bone Count");
                }

                List<Matrix> bindPose = new List<Matrix>();
                List<Matrix> inverseBindPose = new List<Matrix>();
                List<int> skeletonHierarchy = new List<int>();
                // This dictionary to find the name of each bones
                Dictionary<string, int> boneIndices = new Dictionary<string, int>();

                foreach (BoneContent bone in bones)
                {
                    bindPose.Add(bone.Transform);
                    inverseBindPose.Add(Matrix.Invert(bone.AbsoluteTransform));
                    skeletonHierarchy.Add(bones.IndexOf(bone.Parent as BoneContent));
                    boneIndices.Add(bone.Name, boneIndices.Count);
                }

                // Store our extended content in the model tag
                model.Tag = new zEngine.ExtendedObjs.ModelTag(bindPose, inverseBindPose, skeletonHierarchy, boneIndices);
            }

            return model;
        }

        /// <summary>
        /// Bake unwanted transformation into model
        /// so everything end up in the same coord
        /// </summary>
        static void FlattenTransform(NodeContent node, BoneContent skeleton)
        {
            foreach (NodeContent child in node.Children)
            {
                if (child == skeleton)
                    continue;

                MeshHelper.TransformScene(child, child.Transform);
                child.Transform = Matrix.Identity;
                FlattenTransform(child, skeleton);
            }
        }
    }
}